/*
 * Copyright 2006-2008 Michele Mostarda ( michele.mostarda@gmail.com ).
 * All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.graphnav.util;

import java.awt.Point;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

/**
 * Some geometry function utilities.
 */
public class GeomUtils {

    /**
     * Defines a rectangle.
     */
    public static class Rect {

        private double m;
        private double q;

        protected Rect(double m, double q) {
            this.m = m;
            this.q = q;
        }

        public double getM() {
            return m;
        }

        public double getQ() {
            return q;
        }
    }

    /** Indicates no intersection between shapes */
    public static final int NO_INTERSECTION = 0;

    /** Indicates intersection between shapes */
    public static final int COINCIDENT      = 1;

    /** Indicates two lines are parallel */
    public static final int PARALLEL        = 2;
    
     /**
      * Compute the intersection of two line segments.
      * @param a the first line segment
      * @param b the second line segment
      * @param intersect a Point in which to store the intersection point
      * @return the intersection code. One of {@link #NO_INTERSECTION},
      * {@link #COINCIDENT}, or {@link #PARALLEL}.
      */
     public static int intersectLineLine(Line2D a, Line2D b, Point2D intersect) {
         double a1x = a.getX1(), a1y = a.getY1();
         double a2x = a.getX2(), a2y = a.getY2();
         double b1x = b.getX1(), b1y = b.getY1();
         double b2x = b.getX2(), b2y = b.getY2();
         return intersectLineLine(a1x,a1y,a2x,a2y,b1x,b1y,b2x,b2y,intersect);
     }

     /**
      * Compute the intersection of two line segments.
      * @param a1x the x-coordinate of the first endpoint of the first line
      * @param a1y the y-coordinate of the first endpoint of the first line
      * @param a2x the x-coordinate of the second endpoint of the first line
      * @param a2y the y-coordinate of the second endpoint of the first line
      * @param b1x the x-coordinate of the first endpoint of the second line
      * @param b1y the y-coordinate of the first endpoint of the second line
      * @param b2x the x-coordinate of the second endpoint of the second line
      * @param b2y the y-coordinate of the second endpoint of the second line
      * @param intersect a Point in which to store the intersection point
      * @return the intersection code. One of {@link #NO_INTERSECTION},
      * {@link #COINCIDENT}, or {@link #PARALLEL}.
      */
     public static int intersectLineLine(double a1x, double a1y, double a2x,
         double a2y, double b1x, double b1y, double b2x, double b2y,
         Point2D intersect)
     {
         double ua_t = (b2x-b1x)*(a1y-b1y)-(b2y-b1y)*(a1x-b1x);
         double ub_t = (a2x-a1x)*(a1y-b1y)-(a2y-a1y)*(a1x-b1x);
         double u_b  = (b2y-b1y)*(a2x-a1x)-(b2x-b1x)*(a2y-a1y);

         if ( u_b != 0 ) {
             double ua = ua_t / u_b;
             double ub = ub_t / u_b;

             if ( 0 <= ua && ua <= 1 && 0 <= ub && ub <= 1 ) {
                 intersect.setLocation(a1x+ua*(a2x-a1x), a1y+ua*(a2y-a1y));
                 return 1;
             } else {
                 return NO_INTERSECTION;
             }
         } else {
             return ( ua_t == 0 || ub_t == 0 ? COINCIDENT : PARALLEL );
         }
     }

     /**
      * Compute the intersection of a line and a rectangle.
      * @param l1x the first endpoint x of the line
      * @param l1y the first endpoint y of the line
      * @param l2x the second endpoint x of the line
      * @param l2y the second endpoint y of the line
      * @param rx the rectangle x pos
      * @param ry the rectangle y pos
      * @param rw the rectangle width
      * @param rh the rectangle height
      * @param pts a length 2 or greater array of points in which to store
      * the results
      * @return the intersection code. One of {@link #NO_INTERSECTION},
      * {@link #COINCIDENT}, or {@link #PARALLEL}.
      */
     public static int intersectLineRectangle(double l1x, double l1y, double l2x, double l2y, double rx, double ry, double rw, double rh, Point2D[] pts) {
         double mxx = rx + rw, mxy = ry + rh;
         int i = 0;
         if ( intersectLineLine(rx,ry,mxx,ry,l1x,l1y,l2x,l2y,pts[i]) > 0 ) i++;
         if ( intersectLineLine(mxx,ry,mxx,mxy,l1x,l1y,l2x,l2y,pts[i]) > 0 ) i++;
         if ( i == 2 ) return i;
         if ( intersectLineLine(mxx,mxy,rx,mxy,l1x,l1y,l2x,l2y,pts[i]) > 0 ) i++;
         if ( i == 2 ) return i;
         if ( intersectLineLine(rx,mxy,rx,ry,l1x,l1y,l2x,l2y,pts[i]) > 0 ) i++;
         return i;
     }

     /**
      * Compute the intersection of a line and a rectangle.
      * @param l the line
      * @param r the rectangle
      * @param pts a length 2 or greater array of points in which to store
      * the results
      * @return the intersection code. One of {@link #NO_INTERSECTION},
      * {@link #COINCIDENT}, or {@link #PARALLEL}.
      */
     public static int intersectLineRectangle(Line2D l, Rectangle2D r, Point2D[] pts) {
         double a1x = l.getX1(), a1y = l.getY1();
         double a2x = l.getX2(), a2y = l.getY2();
         double mxx = r.getMaxX(), mxy = r.getMaxY();
         double mnx = r.getMinX(), mny = r.getMinY();

         if ( pts[0] == null ) pts[0] = new Point2D.Double();
         if ( pts[1] == null ) pts[1] = new Point2D.Double();

         int i = 0;
         if ( intersectLineLine(mnx,mny,mxx,mny,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
         if ( intersectLineLine(mxx,mny,mxx,mxy,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
         if ( i == 2 ) return i;
         if ( intersectLineLine(mxx,mxy,mnx,mxy,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
         if ( i == 2 ) return i;
         if ( intersectLineLine(mnx,mxy,mnx,mny,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
         return i;
     }

     /**
      * Compute the intersection of a line and a rectangle.
      * @param a1 the first endpoint of the line
      * @param a2 the second endpoint of the line
      * @param r the rectangle
      * @param pts a length 2 or greater array of points in which to store
      * the results
      * @return the intersection code. One of {@link #NO_INTERSECTION},
      * {@link #COINCIDENT}, or {@link #PARALLEL}.
      */
     public static int intersectLineRectangle(Point2D a1, Point2D a2, Rectangle2D r, Point2D[] pts) {
         double a1x = a1.getX(), a1y = a1.getY();
         double a2x = a2.getX(), a2y = a2.getY();
         double mxx = r.getMaxX(), mxy = r.getMaxY();
         double mnx = r.getMinX(), mny = r.getMinY();

         if ( pts[0] == null ) pts[0] = new Point2D.Double();
         if ( pts[1] == null ) pts[1] = new Point2D.Double();

         int i = 0;
         if ( intersectLineLine(mnx,mny,mxx,mny,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
         if ( intersectLineLine(mxx,mny,mxx,mxy,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
         if ( i == 2 ) return i;
         if ( intersectLineLine(mxx,mxy,mnx,mxy,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
         if ( i == 2 ) return i;
         if ( intersectLineLine(mnx,mxy,mnx,mny,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
         return i;
     }

    public static Point getRoundPosition(int centerX, int centerY, int total, int current) {
        final double RADIUS = 100d;
        if(current < 0 || total < 0) {
            throw new IllegalArgumentException();
        }
        if(current > total) {
            throw new IllegalArgumentException();
        }
        final double angle = 2.0d * Math.PI / (double) total * (double) current;
        //final double angle = Math.PI / 2;
        return new Point(centerX + (int) ( Math.cos(angle) * RADIUS ), centerY + (int) (Math.sin(angle) * RADIUS) );
    }

    public static Rect rectThrough(double p1x, double p1y, double p2x, double p2y) {
        final double m = (p2y - p1y) / (p2x - p1x);
        final double q = p1y - m * p1x;
        return new Rect(m,q);
    }

    public static double distanceBetween(Rect r, double px, double py) {
        return ( py - r.m * px - r.q ) / Math.sqrt(1.0d + Math.pow(r.m, 2.0d));
    }

    public static double distanceBetween(double p1x, double p1y, double p2x, double p2y, double tx, double ty) {
        return distanceBetween( rectThrough( p1x, p1y, p2x, p2y ), tx, ty );
    }

    public static boolean inRange(double p1x, double p1y, double p2x, double p2y, double tx, double ty, double maxDistance) {
        double minX, maxX, minY, maxY;
        if(p1x <= p2x) {
            minX = p1x;
            maxX = p2x;
        } else {
            minX = p2x;
            maxX = p1x;
        }
        if(p1y <= p2y) {
            minY = p1y;
            maxY = p2y;
        } else {
            minY = p2y;
            maxY = p1y;
        }
        if(tx < minX || tx > maxX || ty < minY || ty > maxY) {
            return false;
        }
        return Math.abs( distanceBetween(p1x, p1y, p2x, p2y, tx, ty) ) <= maxDistance;
    }

}
